package rolesystem.agentlogic.managers;


import java.util.ArrayList;
import java.util.concurrent.Semaphore;

import rolesystem.agentlogic.parameters.ConfigurationParameters;
import rolesystem.core.RoleException;
import rolesystem.core.RoleRegistration;
import rolesystem.core.RoleSystem;
import rolesystem.middle.managers.CommunicationManager;
import rolesystem.middle.managers.DataOutputManager;
import rolesystem.middle.managers.KnownEnvironmentManager;
import rolesystem.middle.utilityclasses.EventData;
import rolesystem.middle.utilityclasses.MessagePayload;
import rolesystem.middle.utilityclasses.Utils;
import rolesystem.roles.carry.Explorer;
import rolesystem.roles.carry.Master;

public class ContactManager 
{
	private int id;
	
	private RoleSystem roleSystem=null;
	
	private static Semaphore semaphore;
	
	private static boolean firstToEnter=false;
	
	private int master_ID;
	
	private CommunicationManager communicationManager;
	
	static
	{
		ContactManager.semaphore = new Semaphore(1);
	}
	
	public ContactManager(int id,CommunicationManager communicationManager)
	{
		this.id=id;
		this.communicationManager=communicationManager;
	}
	
	public void setRoleSystem(RoleSystem roleSystem)
	{
		this.roleSystem=roleSystem;	
	}
	
	/**
	 * Put the agent to sleep for a number of milliseconds. 
	 * 
	 * @param sleepTime Number of ms that the agent will sleep 
	 * 
	 */	
	private void sleepAgent(int sleepTime) 
	{	
		if(sleepTime==0)
			return;
		
		try
		{
			Thread.sleep(sleepTime);
		} catch(InterruptedException ie) {}
	}
	
	public synchronized static boolean decideMaster() throws RoleException
	{
		boolean master;
		
		semaphore.acquireUninterruptibly();
		
		if(firstToEnter==false)
		{
			master=true;
			firstToEnter=true;
		}
		else
		{
			master=false;
		}
				
		semaphore.release();
	
		return master;	
	}
	
	public void announceMaster(RoleRegistration registration) throws RoleException
	{				
		// Contatto i carrier comunicandogli la mia posizione
		int[] carrierArray = roleSystem.searchForRoleNoWait(Explorer.ROLE_ID);
		
		DataOutputManager.printString("carrierArray.length " + carrierArray.length);
		for(int i=0;i<carrierArray.length;i++)
		{						
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			registration.doAction(Master.iAmMaster(carrierArray[i]));
		}
	}
	
	public void acknowledgeMaster(RoleRegistration registration)
	{		
		while(true)
		{		
			communicationManager.receiveEvent(registration);
			
			EventData data=	communicationManager.requestEvent(Explorer.KE_iAmMaster);	
			if(data!=null)
			{			
				this.master_ID=data.getSender();	
				sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
				DataOutputManager.printString(id + " Master Acknowledged");
				break;
			}
			
		}
		
	}
	
	public void clearOutstandingMessages(RoleRegistration registration) 
	{
		
		while(true)
		{		
			if(communicationManager.receiveEvent(registration)==false)			
				return;
		}
	}
	

	
	public boolean receiveModeChangeMessage(RoleRegistration registration)
	{				
		communicationManager.receiveEvent(registration);
		EventData data= communicationManager.requestEvent(Explorer.KE_syncData);
		
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			KnownEnvironmentManager.mergeWithId(id, data.getContent().getKnownEnvironment());
			//DataOutputManager.printString(id + " Explorer: data received from master ID " + data.getSender());
			return false;
		}
		
		data= communicationManager.requestEvent(Master.KE_moveData);
		
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL/(ConfigurationParameters.NUMBER_OF_AGENTS_EXPL-1));
			KnownEnvironmentManager.mergeWithId(id, data.getContent().getKnownEnvironment());
			//DataOutputManager.printString(id + " Master: data received from " + data.getSender());
			return false;
		}	
		
		data= communicationManager.requestEvent(Explorer.KE_changeRandom);
		
		if(data!=null)
		{	
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			//DataOutputManager.printString(id + " Change mode to random received from " + data.getSender());
			return true;
		}
		
		data= communicationManager.requestEvent(Master.KE_changeRandom);
		
		if(data!=null)
		{	
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			//DataOutputManager.printString(id + " Change mode to random received from " + data.getSender());
			return true;
		}			
		return false;
		
		
	}
	
	public int receiveModeChangeMessageRandom(RoleRegistration registration) 
	{			
		communicationManager.receiveEvent(registration);
		EventData data= communicationManager.requestEvent(Explorer.KE_syncData);
		
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			DataOutputManager.printString(id + " Invalid data received from  " + data.getSender());
			return 0;
		}
		
		data= communicationManager.requestEvent(Master.KE_moveData);
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			DataOutputManager.printString(id + " Invalid data received from  " + data.getSender());
			return 0;
		}
		
		data= communicationManager.requestEvent(Explorer.KE_changeSlave);
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			//DataOutputManager.printString(id + " Explorer: change mode to slave received from " + data.getSender());
			return 2;
		}
		
		data= communicationManager.requestEvent(Explorer.KE_youAreMaster);
		if(data!=null)
		{			
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			//DataOutputManager.printString(id + " Explorer: Master annunce received from " + data.getSender());
			return 1;
		}
		return 0;		
	}
	
	public void sendExplorationData(RoleRegistration registration) throws RoleException
	{			
		int[] carrierArray = roleSystem.searchForRoleNoWait(Master.ROLE_ID);
		
		for(int i=0;i<carrierArray.length;i++)
		{						
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			registration.doAction(Explorer.moveData(carrierArray[i],new MessagePayload(KnownEnvironmentManager.getKnownEnvironments(id))));
		}
	}
	
	public void refreshExplorers(RoleRegistration registration) throws RoleException
	{			
		int[] carrierArray = roleSystem.searchForRoleNoWait(Explorer.ROLE_ID);
		
		for(int i=0;i<carrierArray.length;i++)
		{						
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL/(ConfigurationParameters.NUMBER_OF_AGENTS_EXPL-1));
			registration.doAction(Master.syncData(carrierArray[i],new MessagePayload(KnownEnvironmentManager.getKnownEnvironments(id))));
		}
	}
	
	
	public void randomContact(RoleRegistration registration) throws RoleException
	{
		int[] carrierArray = roleSystem.searchForRoleNoWait(Explorer.ROLE_ID);
		
		int randomNumber= ((int)(Math.random()*(carrierArray.length-1)))+1;
		
		sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
		registration.doAction(Explorer.youAreMaster(carrierArray[randomNumber]));	
		
		ArrayList<Integer> carrierRemove=Utils.arraytoIntegerArrayList(carrierArray);
		
		carrierRemove.remove(new Integer(carrierArray[randomNumber]));

		for(int i=0;i<carrierRemove.size();i++)
		{	
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			registration.doAction(Explorer.youAreSlave((int)carrierRemove.get(i)));
		}
	}
	
	public void masterAndSlaveContact(RoleRegistration registration) throws RoleException
	{
		int[] carrierArray = roleSystem.searchForRoleNoWait(Explorer.ROLE_ID);
		
		for(int i=0;i<carrierArray.length;i++)
		{						
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			registration.doAction(Explorer.changeRandom(carrierArray[i]));
		}
		
		int[] masterArray = roleSystem.searchForRoleNoWait(Master.ROLE_ID);
		
		for(int i=0;i<masterArray.length;i++)
		{						
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			registration.doAction(Explorer.changeRandom(masterArray[i]));
		}	
		
		//registration.doAction(Explorer.changeRandom(registration.whoAmI())); // Message to myself
	}
	
	public boolean receiveMasterAssign(RoleRegistration registration) 
	{
		communicationManager.receiveEvent(registration);
		
		EventData data= communicationManager.requestEvent(Explorer.KE_youAreMaster);
		if(data!=null)
		{			
			//DataOutputManager.printString(id + " Explorer: Master annunce received from " + data.getSender());
			sleepAgent(ConfigurationParameters.COMMUNICATION_LATENCE_EXPL);
			return true;
		}
		else
			return false;	
	}
	
}
